/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997, 1998, 1999  Sam Lantinga

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    Sam Lantinga
    slouken@devolution.com
*/
#include <stdio.h>
#include <string.h>

#include <SDL/SDL.h>
#include "SDL_blit.h"

/* Functions to blit surface using colorkey transparency */

static void BlitBto1Key(int width, int height,
		Uint8 *src, SDL_PixelFormat *srcfmt, int srcskip,
		Uint8 *palmap,
		Uint8 *dst, SDL_PixelFormat *dstfmt, int dstskip)
{
	int c;
	Uint8  byte, bit;

	/* Set up some basic variables */
	srcskip += width-(width+7)/8;

	if ( palmap ) {
		while ( height-- ) {
	    		for ( c=0; c<width; ++c ) {
				if ( (c%8) == 0 ) {
					byte = *src++;
				}
				bit = (byte&0x80)>>7;
				if ( bit != srcfmt->colorkey ) {
				  *dst = palmap[bit];
				}
				dst++;
				byte <<= 1;
			}
			src += srcskip;
			dst += dstskip;
		}
	} else {
		while ( height-- ) {
	    		for ( c=0; c<width; ++c ) {
				if ( (c%8) == 0 ) {
					byte = *src++;
				}
				bit = (byte&0x80)>>7;
				if ( bit != srcfmt->colorkey ) {
				  *dst = bit;
				}
				dst++;
				byte <<= 1;
			}
			src += srcskip;
			dst += dstskip;
		}
	}
}
static void BlitBto2Key(int width, int height,
			Uint8 *src, SDL_PixelFormat *srcfmt, int srcskip,
			Uint8 *palmap,
			Uint8 *dst, int dstskip)
{
	Uint16 *dstp;
	int c;
	Uint8  byte, bit;

	/* Set up some basic variables */
	dstp = (Uint16 *)dst;
	srcskip += width-(width+7)/8;
	dstskip /= 2;

	while ( height-- ) {
	    	for ( c=0; c<width; ++c ) {
			if ( (c%8) == 0 ) {
				byte = *src++;
			}
			bit = (byte&0x80)>>7;
			if ( bit != srcfmt->colorkey ) {
				*dstp=((Uint16 *)palmap)[bit];
			}
			byte <<= 1;
			dstp++;
		}
		src += srcskip;
		dstp += dstskip;
	}
}
static void BlitBto3Key(int width, int height,
			Uint8 *src, SDL_PixelFormat *srcfmt, int srcskip,
			Uint8 *palmap,
			Uint8 *dst, int dstskip)
{
	int c;
	Uint8  byte, bit;

	/* Set up some basic variables */
	srcskip += width-(width+7)/8;

	while ( height-- ) {
	    	for ( c=0; c<width; ++c ) {
			if ( (c%8) == 0 ) {
				byte = *src++;
			}
			bit = (byte&0x80)>>7;
			if ( bit != srcfmt->colorkey ) {
				memcpy(dst, &palmap[bit*4], 3);
			}
			byte <<= 1;
			dst += 3;
		}
		src += srcskip;
		dst += dstskip;
	}
}
static void BlitBto4Key(int width, int height,
			Uint8 *src, SDL_PixelFormat *srcfmt, int srcskip,
			Uint8 *palmap,
			Uint8 *dst, int dstskip)
{
	Uint32 *dstp;
	int c;
	Uint8  byte, bit;

	/* Set up some basic variables */
	dstp = (Uint32 *)dst;
	srcskip += width-(width+7)/8;
	dstskip /= 4;

	while ( height-- ) {
	    	for ( c=0; c<width; ++c ) {
			if ( (c%8) == 0 ) {
				byte = *src++;
			}
			bit = (byte&0x80)>>7;
			if ( bit != srcfmt->colorkey ) {
				*dstp=((Uint32 *)palmap)[bit];
			}
			byte <<= 1;
			dstp++;
		}
		src += srcskip;
		dstp += dstskip;
	}
}
static void Blit1to1Key(int width, int height,
		Uint8 *src, SDL_PixelFormat *srcfmt, int srcskip,
		Uint8 *palmap,
		Uint8 *dst, SDL_PixelFormat *dstfmt, int dstskip)
{
	int c;

	if ( palmap ) {
		while ( height-- ) {
			for ( c=width; c; --c ) {
				if ( *src != srcfmt->colorkey ) {
				  *dst = palmap[*src];
				}
				dst++;
				src++;
			}
			src += srcskip;
			dst += dstskip;
		}
	} else {
		while ( height-- ) {
			for ( c=width; c; --c ) {
				if ( *src != srcfmt->colorkey ) {
				  *dst = *src;
				}
				dst++;
				src++;
			}
			src += srcskip;
			dst += dstskip;
		}
	}
}
static void Blit1to2Key(int width, int height,
			Uint8 *src, SDL_PixelFormat *srcfmt, int srcskip,
			Uint8 *palmap,
			Uint8 *dst, int dstskip)
{
	Uint16 *dstp;
	int c;

	/* Set up some basic variables */
	dstp = (Uint16 *)dst;
	dstskip /= 2;

	while ( height-- ) {
		for ( c=width; c; --c ) {
			if ( *src != srcfmt->colorkey ) {
				*dstp=((Uint16 *)palmap)[*src];
			}
			src++;
			dstp++;
		}
		src += srcskip;
		dstp += dstskip;
	}
}
static void Blit1to3Key(int width, int height,
			Uint8 *src, SDL_PixelFormat *srcfmt, int srcskip,
			Uint8 *palmap,
			Uint8 *dst, int dstskip)
{
	int c, o;

	while ( height-- ) {
		for ( c=width; c; --c ) {
			if ( *src != srcfmt->colorkey ) {
				o = *src * 4;
				dst[0] = palmap[o++];
				dst[1] = palmap[o++];
				dst[2] = palmap[o++];
			}
			src++;
			dst += 3;
		}
		src += srcskip;
		dst += dstskip;
	}
}
static void Blit1to4Key(int width, int height,
			Uint8 *src, SDL_PixelFormat *srcfmt, int srcskip,
			Uint8 *palmap,
			Uint8 *dst, int dstskip)
{
	Uint32 *dstp;
	int c;

	/* Set up some basic variables */
	dstp = (Uint32 *)dst;
	dstskip /= 4;

	while ( height-- ) {
		for ( c=width; c; --c ) {
			if ( *src != srcfmt->colorkey ) {
				*dstp=((Uint32 *)palmap)[*src];
			}
			src++;
			dstp++;
		}
		src += srcskip;
		dstp += dstskip;
	}
}
static void BlitNto1Key(int width, int height,
		Uint8 *src, SDL_PixelFormat *srcfmt, int srcskip,
			Uint8 *palmap, Uint8 *dst, int dstskip)
{
	Uint8 srcbpp;
	int c;
	Uint32 pixel;
	Uint8  sR, sG, sB;

	/* Set up some basic variables */
	srcbpp = srcfmt->BytesPerPixel;

	if ( palmap == NULL ) {
		while ( height-- ) {
			for ( c=width; c; --c ) {
				DISEMBLE_RGB(src, srcbpp, srcfmt, pixel,
								sR, sG, sB);
				if ( pixel != srcfmt->colorkey ) {
				  	/* Pack RGB into 8bit pixel */
				  	*dst = ((sR>>5)<<(3+2))|
						((sG>>5)<<(2)) |
						((sB>>6)<<(0)) ;
				}
				dst++;
				src += srcbpp;
			}
			src += srcskip;
			dst += dstskip;
		}
	} else {
		while ( height-- ) {
			for ( c=width; c; --c ) {
				DISEMBLE_RGB(src, srcbpp, srcfmt, pixel,
								sR, sG, sB);
				if ( pixel != srcfmt->colorkey ) {
				  	/* Pack RGB into 8bit pixel */
				  	*dst = palmap[((sR>>5)<<(3+2))|
							((sG>>5)<<(2))  |
							((sB>>6)<<(0))  ];
				}
				dst++;
				src += srcbpp;
			}
			src += srcskip;
			dst += dstskip;
		}
	}
}
static void BlitNtoNKey(int width, int height,
		Uint8 *src, SDL_PixelFormat *srcfmt, int srcskip,
		Uint8 *dst, SDL_PixelFormat *dstfmt, int dstskip)
{
	Uint8 srcbpp;
	Uint8 dstbpp;
	int c;
	Uint32 pixel;
	Uint8  sR, sG, sB;

	/* Set up some basic variables */
	srcbpp = srcfmt->BytesPerPixel;
	dstbpp = dstfmt->BytesPerPixel;

	while ( height-- ) {
		for ( c=width; c; --c ) {
			DISEMBLE_RGB(src, srcbpp, srcfmt, pixel, sR, sG, sB);
			if ( pixel != srcfmt->colorkey ) {
				  ASSEMBLE_RGB(dst, dstbpp, dstfmt, sR, sG, sB);
			}
			dst += dstbpp;
			src += srcbpp;
		}
		src += srcskip;
		dst += dstskip;
	}
}

void SDL_BlitKey(SDL_BlitInfo *info)
{
	Uint8 *src, *palmap, *dst;
	int width, height;
	int srcskip, dstskip;
	SDL_PixelFormat *srcfmt, *dstfmt;

	width = info->d_width;
	height = info->d_height;
	src = info->s_pixels;
	srcskip = info->s_skip;
	srcfmt = info->src;
	dst = info->d_pixels;
	dstskip = info->d_skip;
	dstfmt = info->dst;
	dst = info->d_pixels;
	palmap = info->table;

	switch (srcfmt->BytesPerPixel) {
	/* srcbpp == 1 -- assume palmap is set up by SDL_MapSurface() */
	case 1:
		if ( srcfmt->BitsPerPixel == 8 ) {
			switch (dstfmt->BytesPerPixel) {
			case 1:
			  if ( dstfmt->BitsPerPixel == 8 ) {
			  	Blit1to1Key(width,height,src,srcfmt,srcskip,
						palmap, dst, dstfmt, dstskip);
			  } else {
				/* FIXME:  Can't blit to bitmaps */
			  }
		  	break;
			case 2:
			  Blit1to2Key(width, height, src, srcfmt, srcskip,
							palmap, dst, dstskip);
			  break;
			case 3:
			  Blit1to3Key(width, height, src, srcfmt, srcskip,
							palmap, dst, dstskip);
			  break;
			case 4:
			  Blit1to4Key(width, height, src, srcfmt, srcskip,
							palmap, dst, dstskip);
			  break;
			default:
			  break;
			}
		} else
		if ( srcfmt->BitsPerPixel == 1 ) {
			switch (dstfmt->BytesPerPixel) {
			case 1:
			  if ( dstfmt->BitsPerPixel == 8 ) {
			  	BlitBto1Key(width,height,src,srcfmt,srcskip,
						palmap, dst, dstfmt, dstskip);
			  } else {
				/* FIXME:  Can't blit to bitmaps */
			  }
		  	break;
			case 2:
			  BlitBto2Key(width, height, src, srcfmt, srcskip,
							palmap, dst, dstskip);
			  break;
			case 3:
			  BlitBto3Key(width, height, src, srcfmt, srcskip,
							palmap, dst, dstskip);
			  break;
			case 4:
			  BlitBto4Key(width, height, src, srcfmt, srcskip,
							palmap, dst, dstskip);
			  break;
			default:
			  break;
			}
		} else {
			/* FIXME:  Unsupported bit-depth */;
		}
	  break;
	case 2:
	case 3:
	case 4:
		switch (dstfmt->BytesPerPixel) {
		case 1:
		  BlitNto1Key(width, height, src, srcfmt, srcskip, 
						palmap, dst, dstskip);
		  break;
		case 2:
		case 3:
		case 4:
		  BlitNtoNKey(width, height, src, srcfmt, srcskip, 
						dst, dstfmt, dstskip);
		  break;
		}
	  break;
	}
}
